package com.siyoumi.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.siyoumi.component.XApp;
import com.siyoumi.component.XBean;
import com.siyoumi.component.XRedis;
import com.siyoumi.component.http.InputData;
import com.siyoumi.component.http.XHttpContext;
import com.siyoumi.entity.EntityBase;
import com.siyoumi.exception.EnumSys;
import com.siyoumi.exception.XException;
import com.siyoumi.mybatispuls.JoinWrapperPlus;
import com.siyoumi.mybatispuls.MapperBase;
import com.siyoumi.util.XDate;
import com.siyoumi.util.XLog;
import com.siyoumi.util.XReturn;
import com.siyoumi.util.XStr;
import com.siyoumi.validator.XValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
public class ServiceImplBase<M extends MapperBase<T>, T extends EntityBase<T>>
        extends ServiceImpl<M, T>
        implements ServiceBase<T, M> {
    //是否为软删除
    protected boolean softDelete() {
        return false;
    }

    @Override
    public M mapper() {
        return getBaseMapper();
    }

    @Override
    public String getEntityKeyName(String key) {
        String contextKey = XStr.concat(getEntityName(), "|entity");
        T entity = XHttpContext.get(contextKey);
        if (entity == null) {
            entity = (T) XBean.newIns(getEntityClass());
            XHttpContext.set(contextKey, entity);
        }

        return entity.getEntityKeyName(key);
    }

    @Override
    public T getEntityLock(String id) {
        MapperBase<T> mapper = mapper();

        QueryWrapper<T> query = q();
        query.eq(fdKey(), id);
        if (softDelete()) {
            query.eq(getEntityKeyName("del"), 0);
        }

        return first(query, true);
    }

    @Override
    public T getEntity(String id) {
        String key = getEntityCacheKey(id, false);
        T entity = XHttpContext.get(key);
        if (entity == null) {
            MapperBase<T> mapper = mapper();

            QueryWrapper<T> query = q();
            query.eq(fdKey(), id);
            if (softDelete()) {
                query.eq(getEntityKeyName("del"), 0);
            }

            entity = first(query);

            XHttpContext.set(key, entity);
        }
        return entity;
    }

    @Override
    public T getEntity(String id, String x) {
        T entity = getEntity(id);
        if (entity == null) {
            return null;
        }

        if (!entity.getX().equals(x)) {
            return null;
        }

        return entity;
    }

    @Override
    public T loadEntity(String id) {
        T entity = getEntity(id);
        if (entity == null) {
            throw new XException(XStr.concat("entity is null, entityName=", getEntityClass().getName(), ", id=", id));
        }

        return entity;
    }

    @Override
    public T loadEntity(String id, String x) {
        T entity = loadEntity(id);
        if (!entity.getX().equals(x)) {
            throw new XException(XStr.concat("x error, entityName=", getEntityName(), ", entity_x=", entity.getX(), ", x=", x, ", id=", id));
        }

        return entity;
    }

    /**
     * T名称
     * com.siyoumi.entity.SysAccount
     * 变
     * SysAccount
     *
     * @return T名称
     */
    protected String getEntityName() {
        String name = getEntityClass().getName();
        String[] nameData = name.split("\\.");

        return nameData[nameData.length - 1];
    }


    @Override
    public String getEntityCacheKey(String keyFix) {
        return getEntityCacheKey(keyFix, true);
    }

    @Override
    public String getEntityCacheKey(String keyFix, Boolean addX) {
        String className = getEntityName();

        String key = XStr.concat("cache_model:", className, "|", keyFix);
        if (addX) {
            String x = XHttpContext.getX();
            key = XStr.concat("cache_model:", className, "|", x, "|", keyFix);
        }
        return key;
    }


    @Override
    public void delEntityCache(String keyFix) {
        String redisKey = getEntityCacheKey(keyFix);
        log.debug("delCache:{}", redisKey);
        XRedis.getBean().del(redisKey);
    }

    @Deprecated
    @Override
    @Transactional(rollbackFor = Exception.class)
    public XReturn saveEntity(InputData inputData, Boolean autoSetId, List<String> ignoreField) {
        //排队项
        if (ignoreField == null) {
            ignoreField = new ArrayList<>();
        }
        ignoreField.add(getEntityKeyName("update_date"));
        ignoreField.add(getEntityKeyName("create_date"));
        if (inputData.isAdminEdit()) {
            // 更新操作不能修改id,x
            ignoreField.add(getEntityKeyName("id"));
            ignoreField.add(getEntityKeyName("x_id"));
        }

        for (String igKey : ignoreField) {
            inputData.remove(igKey);
        }

        T entity = loadEntity(inputData);
        if (inputData.isAdminAdd()) {
            //添加
            if (autoSetId) {
                entity.setAutoID(true);
            }
            if (ignoreField.contains(entity.getEntityKeyName("x_id"))) {
                // 过滤字段存在x，不自己赋值
                //pass
            } else {
                entity.setAttributeVal(entity.getEntityKeyName("x_id"), XHttpContext.getX());
            }
        } else {
            //编辑
            entity.setAttributeVal(fdKey(), inputData.getID());
            if (XStr.isNullOrEmpty(entity.getKey())) {
                return XReturn.getR(EnumSys.MISS_VAL.getErrcode(), "id miss");
            }
        }
        //保存前
        XReturn r = saveEntityBefore(inputData, entity);
        if (r.err()) {
            return r;
        }
        //保存实体
        if (inputData.isAdminAdd()) {
            save(entity);
        } else {
            XLog.debug(this.getClass(), entity.toMap());
            T entitySrc = loadEntity(entity.getKey());
            entity = saveOrUpdatePassEqualField(entitySrc, entity);
            //update(entity, q().eq(fdKey(), entity.getKey()));
        }
        //保存后
        r = saveEntityAfter(inputData, entity);
        //
        r.setData("id", entity.getKey());
        r.setData("entity", entity);
        return r;
    }

    @Override
    public XReturn saveEntity(InputData inputData, Object editEntity, Boolean autoSetId, List<String> ignoreField) {
        XValidator.checkTransaction();

        Map<String, Object> mapEditEntity = XBean.toMap(editEntity);

        //排队项
        if (ignoreField == null) {
            ignoreField = new ArrayList<>();
        }
        ignoreField.add(getEntityKeyName("update_date"));
        ignoreField.add(getEntityKeyName("create_date"));
        if (inputData.isAdminEdit()) {
            // 更新操作不能修改id,x
            ignoreField.add(getEntityKeyName("id"));
            ignoreField.add(getEntityKeyName("x_id"));
            ignoreField.add(getEntityKeyName("acc_id"));
            ignoreField.add(getEntityKeyName("store_id"));
        }

        for (String igKey : ignoreField) {
            mapEditEntity.remove(igKey);
        }

        T entity = loadEntity(mapEditEntity);
        if (inputData.isAdminAdd()) {
            //添加
            if (autoSetId) {
                entity.setAutoID(true);
            }
            if (ignoreField.contains(entity.getEntityKeyName("x_id"))) {
                // 过滤字段存在x，不自己赋值
                //pass
            } else {
                entity.setAttributeVal(entity.getEntityKeyName("x_id"), XHttpContext.getX());
            }
        } else {
            //编辑
            entity.setAttributeVal(fdKey(), inputData.getID());
            if (XStr.isNullOrEmpty(entity.getKey())) {
                return EnumSys.MISS_VAL.getR("id miss");
            }
            T entityOld = first(inputData.getID());
            if (entityOld == null) {
                return EnumSys.ERR_VAL.getR("id异常");
            }
        }

        //保存前
        XReturn r = saveEntityBefore(inputData, entity);
        if (r.err()) {
            return r;
        }
        //保存实体
        if (inputData.isAdminAdd()) {
            save(entity);
        } else {
            log.debug("{}", entity.toMap());
            T entitySrc = loadEntity(entity.getKey());
            entity = saveOrUpdatePassEqualField(entitySrc, entity);
            //update(entity, q().eq(fdKey(), entity.getKey()));
        }
        //保存后
        r = saveEntityAfter(inputData, entity);
        //
        r.setData("id", entity.getKey());
        r.setData("entity", entity);
        return r;
    }

    @Override
    public XReturn saveEntityBefore(InputData inputData, T entity) {
        return XReturn.getR(0);
    }

    @Override
    public XReturn saveEntityAfter(InputData inputData, T entity) {
        return XReturn.getR(0);
    }

    @Override
    public BigDecimal sum(JoinWrapperPlus<T> query, String sumSql) {
        query.select(XStr.concat("IFNULL(sum(", sumSql, "),0) AS total"));
        Map<String, Object> map = getBaseMapper().firstMap(query);
        return (BigDecimal) map.get("total");
    }

    @Override
    public Long count(JoinWrapperPlus<T> query) {
        query.select(XStr.concat("count(*) AS total"));
        Map<String, Object> map = getBaseMapper().firstMap(query);
        return (Long) map.get("total");
    }


    @Override
    public T loadEntity(Map<String, Object> map) {
        return XBean.fromMap(map, getEntityClass());
    }

    @Override
    public T saveOrUpdatePassEqualField(T entitySrc, T entitySaveOrUpdate, List<String> ignoreField) {
        Boolean doSet = false; //是否需要操作
        if (entitySrc != null) {
            log.debug("更新操作");

            if (ignoreField == null) {
                ignoreField = new ArrayList<>();
            }
            ignoreField.add("class");
            ignoreField.add("key");
            ignoreField.add("x");
            ignoreField.add("keyObj");
            ignoreField.add(getEntityKeyName("update_date"));
            ignoreField.add(getEntityKeyName("create_date"));
            Map<String, Object> mapSaveOrUpdate = XBean.toMap(entitySaveOrUpdate, ignoreField);
            //update_date 不更新
            entitySaveOrUpdate.setAttributeVal(getEntityKeyName("update_date"), null);
            //
            for (Map.Entry<String, Object> entry : mapSaveOrUpdate.entrySet()) {
                Object v = entry.getValue(); //值
                String k = entry.getKey(); //字段名
                if (v == null) continue; //null的数据不会更新 mybais-puls机制
                if (v.equals(entitySrc.getAttributeVal(k))) {
                    //值相同，字段不更新
                    entitySaveOrUpdate.setAttributeVal(k, null);
                } else {
                    //不相同，更新源实体
                    entitySrc.setAttributeVal(k, v);
                    doSet = true;
                }
            }
            entitySaveOrUpdate.setAttributeVal(entitySrc.getKey(), null);

            if (doSet) {
                QueryWrapper<T> query = q();
                query.eq(fdKey(), entitySrc.getKey());
                update(entitySaveOrUpdate, query);
            }
        } else {
            log.debug("添加操作");
            doSet = true;

            save(entitySaveOrUpdate);

            //entitySrc = XBean.newIns(getEntityClass());
            //XBean.copyProperties(entitySaveOrUpdate, entitySrc);
            entitySrc = entitySaveOrUpdate;
        }

        log.debug("doSet: {}", doSet);
        XHttpContext.set("doSet", doSet);

        return entitySrc;
    }


    public XReturn delete(String id) {
        XValidator.checkTransaction();

        delete(List.of(id));

        return EnumSys.OK.getR();
    }

    /**
     * 删除
     *
     * @param ids
     */
    public boolean delete(List<String> ids) {
        XValidator.checkTransaction();

        if (ids == null || ids.size() <= 0) {
            XValidator.err(20396, "ids is null or empty");
        }

        if (softDelete()) {
            //软删除
            return update().eq(getEntityKeyName("x_id"), XHttpContext.getX())
                    .in(fdKey(), ids)
                    .set(getEntityKeyName("del"), XDate.toS())
                    .update();

        } else {
            QueryWrapper<T> query = q().eq(getEntityKeyName("x_id"), XHttpContext.getX());
            query.in(fdKey(), ids);
            List<T> list = list(query);
            return removeBatchByIds(list);
        }
    }
}
